package com.wydpp.gb28181.commander;

import com.wydpp.config.SystemConfig;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264;

@Component
public class FfmpegCommander implements IFfmpegCommander {

    private final Logger logger = LoggerFactory.getLogger(FfmpegCommander.class);

    @Autowired
    private SystemConfig systemConfig;

    private static final Map<String, Process> processMap = new ConcurrentHashMap<>();

    public String pushStream(String callId, String filePath, String ip, int port) {
        String command = systemConfig.getFfmpegPath() + " " +
                systemConfig.getFfmpegPushStreamCmd().replace("{filePath}", filePath).replace("{ip}", ip).replace("{port}", port + "");
        logger.info("callId={},\r\n推流命令={}", callId, command);
        Runtime runtime = Runtime.getRuntime();
        try {
            new Thread(() -> {
                int code = 0;
                try {
                    Process process = runtime.exec(command);
                    processMap.put(callId, process);
                    InputStream errorInputStream = process.getErrorStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(errorInputStream));
                    StringBuffer errorStr = new StringBuffer();
                    String str;
                    while ((str = reader.readLine()) != null) {
                        errorStr.append(str);
                        logger.debug(str);
                    }
                    code = process.waitFor();
                    logger.info("推流已结束,callId={}", callId);
                } catch (Exception e) {
                    logger.error("ffmpeg推流异常!", e);
                }
                System.out.println(code);
            }).start();
            return command;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    public String pushVideoStream(String callId, String filePath, String ip, int port) {
        // 设置 RTSP URL
        String rtspUrl = "rtsp://lxf:aaaa0000@192.168.10.66:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1";
        String rtpUrl = "rtp://" + ip + ":" + port;
        logger.info(rtpUrl + "推流地址:{},推流端口:{}", ip, port);
        try {
            rtspToRtp(rtspUrl, rtpUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    private void rtspToRtp(String rtspUrl, String rtpUrl) {
        try {
            rtpUrl = "rtp://192.168.10.99:15050";
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
            grabber.start();
            logger.info("启动grabber");
            double videoFrameRate = grabber.getVideoFrameRate();
            int imageWidth = grabber.getImageWidth();
            int imageHeight = grabber.getImageHeight();
            int audioChannels = grabber.getAudioChannels();
            logger.info("Video info -> fps " + videoFrameRate + " imageWidth:" + imageWidth + " imageHeight:" + imageHeight + " audioChannels:" + audioChannels);

            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtpUrl, imageWidth, imageHeight, audioChannels);

            recorder.setFormat("rtp_mpegts");//推rtmp这里是必须设置成flv，这里是推rtp，所以必须是rtp_mpegts
            recorder.setVideoCodec(AV_CODEC_ID_H264);
            recorder.setVideoBitrate(1000 * 1000); // 1 Mbps
            recorder.setFrameRate(videoFrameRate);
            recorder.start();
            logger.info("启动recorder");

            Frame frame = null;
            while ((frame = grabber.grab()) != null){
                recorder.record(frame);
            }
            recorder.stop();
            recorder.release();
            grabber.stop();
            grabber.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void closeStream(String callId) {
        logger.info("关闭推流:{}", callId);
        if (StringUtils.isEmpty(callId)) {
            closeAllStream();
        } else if (processMap.containsKey(callId)) {
            processMap.get(callId).destroy();
        } else {
            logger.info("没有推流要关闭!");
        }
    }

    public void closeAllStream() {
        logger.info("关闭所有推流");
        processMap.entrySet().stream().forEach(entry -> {
            entry.getValue().destroy();
        });
    }
}
